#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/errno.h>
#include <string.h>

#define SERVER_PORTNUM 2020
#define MSGLEN 256
#define TICKET_AVAIL 0
#define oops(m){perror(m);}
#define RECLAIM_INTERVAL 15
#define BUFSIZE 256

int MaxUsers[100];
char PZarr[100][100] ;
int ticket_array[100][100];
int maxCnt = 0;
int sd = -1;
int num_tickets_out = 0;
int make_dgram_server_socket(int portnum);
void ticket_reclaim();
void narrate(char * msg1, char * msg2, struct sockaddr_in * clientp);
char * do_goodbye(char * msg_p);
char * do_hello(char * msg_p);
void handle_request(char * req, struct sockaddr_in * client, socklen_t addlen);
void _shutdown();
void free_all_tickets();
static char * do_validate(char * msg);
int exist_PZ(char * PZ);
int setup(char (* _PZarr)[12], int _maxCnt, int * _MaxUsers);

int exist_PZ(char * PZ){
    for (int i = 0; i < maxCnt; i++){
        if (strcmp(PZ, PZarr[i]) == 0)
            return i;
    }
    return -1;
}

static char * do_validate(char * msg){
    int pid, slot, no;
    char PZ[100];
    if (sscanf(msg+5, "%d. %d %s", &pid, &slot, PZ) == 3 && (no = exist_PZ(PZ)) != -1 && ticket_array[no][slot] == pid)
        return "GOOD Valid ticket";
    narrate("Bogus ticket", msg+5, NULL);
    return "FAIL invalid ticket";
}


int setup(char (* _PZarr)[12], int _maxCnt, int * _MaxUsers){
    sd = make_dgram_server_socket(SERVER_PORTNUM);
    if (sd == -1)
        oops("make socket");
    maxCnt = _maxCnt;
    for (int i = 0; i < maxCnt; i++){
        strcpy(PZarr[i], _PZarr[i]);
        MaxUsers[i] = _MaxUsers[i];
    }
    free_all_tickets();
    return sd;
}

void free_all_tickets(){
    int i;
    for (i = 0; i < 100; i++){
        for (int q = 0; q < 100; q++){
            ticket_array[i][q] = TICKET_AVAIL;
        }
    }
}

void _shutdown(){
    close(sd);
}

void handle_request(char * req, struct sockaddr_in * client, socklen_t addlen){
    char * response;
    int ret;
    if (strncmp(req, "HELO", 4)==0)
        response = do_hello(req);
    else if (strncmp(req, "GBYE", 4) == 0)
        response = do_goodbye(req);
    else if (strncmp(req, "VALD", 4) == 0)
        response = do_validate(req);
    else
        response = "FAIL invalid request";
    narrate("SAID:", response, client);
    ret = sendto(sd, response, strlen(response), 0, (struct sockaddr*)client, addlen);
    if (ret == -1)
        perror("SERVER sendto failed");
}

char * do_hello(char * msg_p){
    int x;
    char PZ[100];
    int temp, no;
    sscanf(msg_p+5, "%d %s", &temp, PZ);
    if ((no = exist_PZ(PZ)) == -1)
        return "FAIL bad license";
    static char replybuf[MSGLEN];
    if (num_tickets_out >= MaxUsers[no])
        return ("FAIL to tickets available");
    for (x = 0; x < MaxUsers[no] && ticket_array[no][x] != TICKET_AVAIL; x++);
    if (x == MaxUsers[no]){
        narrate("database corrupt", "", NULL);
        return "FAIL database corrupt";
     }
     ticket_array[no][x] = atoi((msg_p+5));
     sprintf(replybuf, "TICK %d. %d", ticket_array[no][x], x);
     num_tickets_out++;
     return replybuf;
}

char * do_goodbye(char * msg_p){
    int pid, slot;
    char PZ[100];
    int no;
    if ((sscanf((msg_p+5), "%d. %d %s", &pid, &slot, PZ) != 3) || (no = exist_PZ(PZ))== -1 ||  
        (ticket_array[no][slot] != pid)){
        narrate("Bogus ticket", msg_p+5, NULL);
        return "FAIL invalid ticket";
    }
    ticket_array[no][slot] = TICKET_AVAIL;
    num_tickets_out--;
    return "THNX See ya!";
}
void narrate(char * msg1, char * msg2, struct sockaddr_in * clientp){
    fprintf(stderr, "SERVER: %s %s", msg1, msg2);
    if (clientp)
        fprintf(stderr, "(%s: %d)", inet_ntoa(clientp->sin_addr),
        ntohs(clientp->sin_port));
    putc('\n', stderr);
}
        
void ticket_reclaim(){
    int i;
    char tick[BUFSIZE];
    for (int i = 0; i < maxCnt; i++){
	    for (int q = 0; q < MaxUsers[i]; q++){
			if ((ticket_array[i][q] != TICKET_AVAIL) && (kill(ticket_array[i][q], 0) == -1) && (errno == ESRCH)){
				sprintf(tick, "%d. %d", ticket_array[i][q], q);
				narrate("freeing", tick, NULL);
				ticket_array[i][q] = TICKET_AVAIL;
				num_tickets_out--;
			}
	    }
    }
    alarm(RECLAIM_INTERVAL);
}
